package com.css.common.core.utils.sign;

import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * rsa签名工具类
 * v1.0.0
 * css
 */
public class SignUtils {

    public static final String SIGN_TYPE_RSA = "RSA";
    public static final String SIGN_TYPE_RSA2 = "RSA2";
    public static final String CHARSET_DEFAULT = "UTF-8";

    public static boolean isSupportSignType(String signType){
        if(!SIGN_TYPE_RSA.equalsIgnoreCase(signType)
                &&!SIGN_TYPE_RSA2.equalsIgnoreCase(signType)){
            return false;
        }else{
            return true;
        }
    }
    /**
     * 验证签名是否正确
     */
    public static boolean isSignatureValid(String context, String sign, String publicKey, String signType)
            throws Exception {
        try {
            if (SIGN_TYPE_RSA.equalsIgnoreCase(signType)) {
                return rsaCheckContent(context, sign, publicKey);
            } else if (SIGN_TYPE_RSA2.equalsIgnoreCase(signType)) {
                return rsa256CheckContent(context, sign, publicKey);
            } else {
                throw new Exception("不支持得签名类型" + signType);
            }
        } catch (Exception e) {
            throw new Exception("获取接入渠道服务权限信息异常");
        }
    }

    /**
     * 生成签名方法
     */
    public static String generateSigned(String content, String privateKey, String signType) throws Exception {
        if (SIGN_TYPE_RSA.equalsIgnoreCase(signType)) {
            return rsaSign(content, privateKey, CHARSET_DEFAULT);
        } else if (SIGN_TYPE_RSA2.equalsIgnoreCase(signType)) {
            return rsa256Sign(content, privateKey, CHARSET_DEFAULT);
        } else {
            throw new Exception("签名类型不支持signType=" + signType);
        }
    }

    public static String rsa256Sign(String content, String privateKey, String charset) throws Exception {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes()));
            java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA");
            signature.initSign(priKey);
            signature.update(content.getBytes(charset));
            byte[] signed = signature.sign();
            return new String(Base64.encodeBase64(signed));
        } catch (Exception e) {
            throw new Exception("生成rsa2签名失败RSAcontent = " + content, e);
        }

    }

    /**
     * sha1WithRsa 加签
     *
     * @param content
     * @param privateKey
     * @param charset
     * @return
     * @throws Exception
     */
    public static String rsaSign(String content, String privateKey, String charset) throws Exception {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes()));
            java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
            signature.initSign(priKey);
            signature.update(content.getBytes(charset));
            byte[] signed = signature.sign();
            return new String(Base64.encodeBase64(signed));
        } catch (InvalidKeySpecException ie) {
            throw new Exception("RSA私钥格式不正确，请检查是否正确配置了PKCS8格式的私钥", ie);
        } catch (Exception e) {
            throw new Exception("生成rsa签名失败RSAcontent = " + content, e);
        }
    }

    private static PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
        if (ins == null || "".equals(algorithm)) {
            return null;
        }
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        byte[] encodedKey = StreamUtils.readText(ins).getBytes();
        encodedKey = Base64.decodeBase64(encodedKey);
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }

    private static boolean rsa256CheckContent(String content, String sign, String publicKey) throws Exception {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
            java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes("UTF-8"));
            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new Exception("RSAcontent = " + content + ",sign=" + sign, e);
        }
    }

    private static boolean rsaCheckContent(String content, String sign, String publicKey) throws Exception {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
            java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new Exception("RSAcontent = " + content + ",sign=" + sign, e);
        }
    }

    private static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        StringWriter writer = new StringWriter();
        StreamUtils.io(new InputStreamReader(ins), writer);
        byte[] encodedKey = writer.toString().getBytes();
        encodedKey = Base64.decodeBase64(encodedKey);
        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }

}
